package cloudspace.vm.io.console;

import java.io.OutputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;


/**
 * An interface for info Streams. These info streams provide lines of console
 * output to the view. The number of lines that can be stored in an info stream
 * is limited by Long.MAX_SIZE.
 */
public class InfoStream extends OutputStream
{
    // private long lastPrint;
    private static final int MAXCACHESIZE = 1000;

    private String streamID;

    /** The timestamp pat. */
    // private static final Pattern timestampPat = Pattern
    // .compile("\\{\\s*([^\\}]+)\\s*\\}.*");;

    /** The date format. */
    private static final DateFormat dateFormat = new SimpleDateFormat( "HH:mm dd/MM/yy" );

    String partialLine;

    private MemoryManagedLineCache lineCache;

    private int currentLine;


    /**
     * This gets the lowest line number stored in this line cache.
     * 
     * @return the lowest index accessible
     */
    public long getLineCacheStart()
    {
        return lineCache.getStartIndex();
    }


    /**
     * Instantiates a new abstract info stream. This stream is automatically
     * registered to the console.
     * 
     * @param identifier
     *            the identifier
     */
    public InfoStream( String identifier )
    {
        super();
        streamID = identifier;
        init();
    }


    private void init()
    {
        currentLine = 0;
        partialLine = "";
        lineCache = new MemoryManagedLineCache( MAXCACHESIZE );
        // lastPrint = 0;

    }


    @Override
    public void write( int b )
    {

        // lastPrint = Calendar.getInstance().getTime().getTime();
        if ( (char)b == '\n' )
        {
            completeLine( partialLine );
            partialLine = "";
        }
        else
        {
            partialLine += (char)b;
        }
    }


    private synchronized void completeLine( String line )
    {
        // this is a backup to flush the stream if the counter goes too high
        if ( currentLine == Long.MAX_VALUE )
            init();
        currentLine++;
        Calendar cal = Calendar.getInstance();
        String curTime = dateFormat.format( cal.getTime() );
        InfoStreamLine isl = convert( "{" + curTime + "}", line, cal.getTime()
            .getTime() );
        if ( isl != null )
        {
            lineCache.add( isl );
        }
        else
        {
            currentLine--;
        }

    }


    /**
     * This converts a raw line to a properly formatted line for output. This
     * appends timestamp and line number.
     * 
     * @param readLine
     *            the line to be properly formatted
     * 
     * @return a fully initialized info stream.
     */
    private InfoStreamLine convert(
        String tStamp,
        String readLine,
        long timeStamp )
    {
//        if ( !readLine.equals( "" ) )
//        {

            try
            {
                return new InfoStreamLine( streamID,
                    tStamp,
                    readLine,
                    timeStamp,
                    currentLine );

            }
            catch ( IllegalStateException e )
            {
                System.err.println( "Internal Matcher does not fit: "
                    + readLine );
            }
//        }
        return null;
    }


    /**
     * Get the current line number for the next line in the info stream
     * 
     * @return the current line number
     */
    public long getLineNumber()
    {
        return currentLine;
    }


    /**
     * Get all the lines stored in the InfoStream starting at a specific index
     * 
     * @param indexLine
     *            index to start the get command from
     * @return a list of info stream lines starting at the line number passed
     * @throws FlushedException
     *             If the info stream line has been flushed, this exception is
     *             thrown to notify any methods accessing the getLines method
     *             that it must reset it's index
     */
    public List<InfoStreamLine> getLines( long indexLine )
        throws FlushedException
    {
        if ( indexLine > lineCache.size() )
        {
            throw new FlushedException();
        }
        List<InfoStreamLine> subLines = new ArrayList<InfoStreamLine>();
        subLines.addAll( lineCache.subList( indexLine, lineCache.size() ) );
        return subLines;
    }


    /**
     * Accesses the stream id for the info stream.
     * 
     * @return the stream id.
     */
    public String getStreamID()
    {
        return streamID;
    }
}